{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "\n",
    "import time\n",
    "\n",
    "from utils import *\n",
    "from mont import *\n",
    "\n",
    "PAIR = 1  # physical core ID\n",
    "CORES = scan_ht_cores()[PAIR]\n",
    "platform = get_uarch()\n",
    "MONT_BASE = f'{BUILD_DIR}/mont-'\n",
    "KASLR_BIN = f'{BUILD_DIR}/kaslr'\n",
    "OSSL_DIR = 'ossl'\n",
    "RES_DIR = Path('results')\n",
    "VER = 'bino'\n",
    "\n",
    "width = 80\n",
    "period = 200\n",
    "n_samples = 70\n",
    "drop_rate = .9\n",
    "anom_thresh = 1000\n",
    "\n",
    "if not RES_DIR.exists():\n",
    "    RES_DIR.mkdir()\n",
    "assert(RES_DIR.is_dir())\n",
    "\n",
    "def run_mont(version='bino', smt_pair=0, ossl_dir=OSSL_DIR, data_file='-', oracle_file='ts.tsv'):\n",
    "    cores = scan_ht_cores()[smt_pair]\n",
    "    bin_path = MONT_BASE + version\n",
    "    cmd = [bin_path, 'mont', cores[0], cores[1], ossl_dir]\n",
    "    return run_once(cmd, data_file, oracle_file)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "boundary 150\n",
      "signals 40\n",
      "eval 150\n"
     ]
    }
   ],
   "source": [
    "# *** collect execution traces *** (skip it if you have collected)\n",
    "# we will collect more traces than what we actually use \n",
    "# because some traces are not suitable for training\n",
    "for niter, use in [(150, 'boundary'), (40, 'signals'), (150, 'eval')]:\n",
    "    data_l = []  # measured raw latencies\n",
    "    oracle_l = []  # oracle iteration boundaries\n",
    "    print(use, niter)\n",
    "    for _ in range(niter):\n",
    "        data, oracle = run_mont(version=VER, smt_pair=PAIR)\n",
    "        data_l.append(data)\n",
    "        oracle_l.append(oracle)\n",
    "        time.sleep(.1)\n",
    "        save_raw_data(VER, use, data_l, oracle_l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "54Data trace contains too extreme anomalies\n",
      "70Data trace contains too extreme anomalies\n",
      "101\n",
      "(472000, 160) (472000,)\n",
      "-1:  75.81%\n",
      "1:  24.19%\n",
      "0.9572457627118645\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "RandomForestClassifier(max_depth=30, max_features='log2', min_samples_split=5,\n",
       "                       n_estimators=400, n_jobs=-1)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# *** train iteration boundary classifier *** (skip it if it's trained)\n",
    "iters = 100\n",
    "raw_results, oracles = load_raw_data(VER, 'boundary')\n",
    "gen_boundary_training(iters, VER, width, period, drop_rate,\n",
    "                      raw_results=raw_results, oracles=oracles,\n",
    "                      anomaly_thresh=anom_thresh)\n",
    "train_boundary_model(best_boundary_params, VER, width, period)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "26Data trace contains too extreme anomalies\n",
      "29(17100, 70) (17100,)\n",
      "0.0:  49.74%\n",
      "1.0:  50.26%\n",
      "0.9900584795321637\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "RandomForestClassifier(max_depth=30, max_features='log2', min_samples_split=5,\n",
       "                       n_estimators=400, n_jobs=-1)"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# *** train signal classifier *** (skip it if it's trained)\n",
    "iters = 30\n",
    "raw_results, oracles = load_raw_data(VER, 'signals')\n",
    "data, oracle = gen_signal_training(VER, n_samples=n_samples, period=period,\n",
    "                                   iters=iters, raw_results=raw_results,\n",
    "                                   oracles=oracles, anomaly_thresh=anom_thresh)\n",
    "train_signal_model(best_signal_params, VER, n_samples=n_samples, period=period)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Predicted tick rate: 12869.43231441048; oracle: 12877.2357403545\n",
      "1: 98.95% 99.12% (Oracle)\n",
      "Predicted tick rate: 12999.780701754386; oracle: 13012.424792841442\n",
      "2: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12876.344086021505; oracle: 12880.25\n",
      "3: 99.47% 99.47% (Oracle)\n",
      "Predicted tick rate: 12893.205944798301; oracle: 12890.014234875445\n",
      "4: 99.65% 99.65% (Oracle)\n",
      "Predicted tick rate: 12992.583732057416; oracle: 13020.769911504425\n",
      "5: 97.72% 99.12% (Oracle)\n",
      "Predicted tick rate: 12877.114427860697; oracle: 12883.588961010879\n",
      "6: 98.60% 98.60% (Oracle)\n",
      "Predicted tick rate: 12869.37354988399; oracle: 12862.128975265017\n",
      "7: 99.30% 99.82% (Oracle)\n",
      "Predicted tick rate: 13018.181818181818; oracle: 12822.95406360424\n",
      "Boundaries are highly unreliable. 1\n",
      "Predicted tick rate: 12863.44537815126; oracle: 12861.143872113676\n",
      "9: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12859.825327510916; oracle: 12853.800711743772\n",
      "10: 98.77% 98.95% (Oracle)\n",
      "Predicted tick rate: 12885.677083333334; oracle: 12892.089768291115\n",
      "11: 96.14% 98.42% (Oracle)\n",
      "Predicted tick rate: 13027.142857142857; oracle: 13086.371609629774\n",
      "12: 97.89% 97.72% (Oracle)\n",
      "Predicted tick rate: 12841.336633663366; oracle: 12840.890265486725\n",
      "13: 97.89% 98.42% (Oracle)\n",
      "Predicted tick rate: 12853.703703703704; oracle: 12855.151515151516\n",
      "14: 98.42% 98.77% (Oracle)\n",
      "Predicted tick rate: 13038.888888888889; oracle: 13263.2304964539\n",
      "15: 50.35% 95.96% (Oracle)\n",
      "Predicted tick rate: 12921.374045801527; oracle: 12928.620141342757\n",
      "16: 95.09% 96.32% (Oracle)\n",
      "Predicted tick rate: 12838.247863247863; oracle: 12835.623008849558\n",
      "17: 98.95% 99.30% (Oracle)\n",
      "Predicted tick rate: 12815.547703180213; oracle: 12811.49171559184\n",
      "18: 95.61% 96.84% (Oracle)\n",
      "Predicted tick rate: 12880.167014613779; oracle: 12882.14768683274\n",
      "19: 98.77% 99.12% (Oracle)\n",
      "Predicted tick rate: 12949.453551912567; oracle: 12956.834132830578\n",
      "20: 98.25% 99.12% (Oracle)\n",
      "Predicted tick rate: 12884.738955823294; oracle: 12889.233628318583\n",
      "21: 98.42% 98.42% (Oracle)\n",
      "Predicted tick rate: 12843.122676579926; oracle: 12828.500891265598\n",
      "Too many iteration boundaries!\n",
      "Predicted tick rate: 12826.587301587302; oracle: 12824.400709219859\n",
      "23: 99.65% 99.82% (Oracle)\n",
      "Predicted tick rate: 12873.218142548596; oracle: 12868.782300884955\n",
      "24: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12807.52427184466; oracle: 12798.211367673179\n",
      "25: 98.60% 98.95% (Oracle)\n",
      "Predicted tick rate: 12864.462809917355; oracle: 12864.666666666666\n",
      "26: 99.47% 99.30% (Oracle)\n",
      "Predicted tick rate: 12793.595041322315; oracle: 12789.26429315141\n",
      "27: 99.47% 99.65% (Oracle)\n",
      "Predicted tick rate: 12834.437086092716; oracle: 12821.778852805031\n",
      "28: 97.72% 98.95% (Oracle)\n",
      "Predicted tick rate: 12905.24017467249; oracle: 12907.762411347518\n",
      "29: 98.60% 99.47% (Oracle)\n",
      "Predicted tick rate: 12803.901437371664; oracle: 12795.811722912966\n",
      "30: 99.30% 99.30% (Oracle)\n",
      "Predicted tick rate: 12909.1796875; oracle: 12918.780918727914\n",
      "31: 99.65% 99.82% (Oracle)\n",
      "Predicted tick rate: 12807.322175732217; oracle: 12803.051344052516\n",
      "32: 99.12% 99.47% (Oracle)\n",
      "Predicted tick rate: 12858.09312638581; oracle: 12856.330476377638\n",
      "33: 98.77% 99.65% (Oracle)\n",
      "Predicted tick rate: 13074.038461538461; oracle: 13138.93213305134\n",
      "34: 96.32% 98.42% (Oracle)\n",
      "Predicted tick rate: 12784.334203655353; oracle: 12770.7625\n",
      "35: 98.95% 98.60% (Oracle)\n",
      "Predicted tick rate: 12897.546012269939; oracle: 12891.112299465241\n",
      "36: 99.12% 99.47% (Oracle)\n",
      "Predicted tick rate: 12893.67681498829; oracle: 12887.870921132997\n",
      "37: 98.25% 99.12% (Oracle)\n",
      "Predicted tick rate: 12938.539553752535; oracle: 12944.581284727023\n",
      "38: 99.12% 99.30% (Oracle)\n",
      "Predicted tick rate: 12837.250554323726; oracle: 12835.306737588653\n",
      "39: 99.12% 99.30% (Oracle)\n",
      "Predicted tick rate: 12858.333333333334; oracle: 12849.317375886525\n",
      "40: 95.09% 97.89% (Oracle)\n",
      "Predicted tick rate: 12814.087759815242; oracle: 12807.579040852575\n",
      "41: 98.25% 99.12% (Oracle)\n",
      "Predicted tick rate: 12807.243460764588; oracle: 12803.966131907307\n",
      "42: 99.65% 99.65% (Oracle)\n",
      "Predicted tick rate: 12792.421052631578; oracle: 12786.901060070672\n",
      "43: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12883.682008368201; oracle: 12884.502654867256\n",
      "44: 98.95% 99.65% (Oracle)\n",
      "Predicted tick rate: 12804.109589041096; oracle: 12797.514184397163\n",
      "45: 98.07% 99.12% (Oracle)\n",
      "Predicted tick rate: 12816.666666666666; oracle: 12838.472468916518\n",
      "46: 97.72% 99.30% (Oracle)\n",
      "Predicted tick rate: 12881.219512195123; oracle: 12875.313274336284\n",
      "47: 96.14% 96.49% (Oracle)\n",
      "Predicted tick rate: 12845.633187772926; oracle: 12844.34813499112\n",
      "48: 98.42% 98.42% (Oracle)\n",
      "Predicted tick rate: 12962.8; oracle: 12971.22261484099\n",
      "49: 98.60% 99.12% (Oracle)\n",
      "Predicted tick rate: 12819.912472647702; oracle: 12812.767874975483\n",
      "50: 98.77% 99.12% (Oracle)\n",
      "Predicted tick rate: 12874.496644295303; oracle: 12898.358288770054\n",
      "51: 93.33% 98.42% (Oracle)\n",
      "Predicted tick rate: 12919.29046563193; oracle: 12924.086725663718\n",
      "52: 98.95% 99.47% (Oracle)\n",
      "Predicted tick rate: 12976.701570680629; oracle: 12991.053285968028\n",
      "Too many iteration boundaries!\n",
      "Predicted tick rate: 12832.30452674897; oracle: 12832.134521449274\n",
      "54: 99.30% 99.47% (Oracle)\n",
      "Predicted tick rate: 12864.915966386554; oracle: 12868.486725663717\n",
      "Too many iteration boundaries!\n",
      "Predicted tick rate: 13069.270833333334; oracle: 13100.903816027134\n",
      "56: 98.95% 99.30% (Oracle)\n",
      "Predicted tick rate: 12887.163561076604; oracle: 12888.07610619469\n",
      "57: 99.47% 99.47% (Oracle)\n",
      "Predicted tick rate: 12917.154811715482; oracle: 12920.916221033867\n",
      "58: 98.42% 99.47% (Oracle)\n",
      "Predicted tick rate: 12949.164677804296; oracle: 12959.45035460993\n",
      "59: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12891.489361702128; oracle: 12981.697879858657\n",
      "60: 75.79% 97.19% (Oracle)\n",
      "Predicted tick rate: 12885.622317596566; oracle: 12881.807079646018\n",
      "Too many iteration boundaries!\n",
      "Predicted tick rate: 13051.333333333334; oracle: 13111.442477876106\n",
      "62: 97.37% 98.42% (Oracle)\n",
      "Predicted tick rate: 13018.918918918918; oracle: 13034.96619217082\n",
      "63: 98.25% 98.42% (Oracle)\n",
      "Predicted tick rate: 12830.76923076923; oracle: 12824.122557726465\n",
      "64: 98.42% 98.42% (Oracle)\n",
      "Predicted tick rate: 12865.675675675675; oracle: 12858.614564831261\n",
      "65: 54.74% 99.30% (Oracle)\n",
      "Predicted tick rate: 12885.377358490567; oracle: 12892.403539823008\n",
      "66: 98.60% 99.47% (Oracle)\n",
      "Predicted tick rate: 12910.888888888889; oracle: 12912.148936170213\n",
      "67: 98.42% 99.12% (Oracle)\n",
      "Predicted tick rate: 13073.454545454546; oracle: 13126.54351687389\n",
      "68: 99.47% 99.65% (Oracle)\n",
      "Predicted tick rate: 12877.103718199609; oracle: 12878.985840707965\n",
      "69: 99.30% 99.82% (Oracle)\n",
      "Predicted tick rate: 13000.0; oracle: 12833.1243339254\n",
      "Boundaries are highly unreliable. 0\n",
      "Predicted tick rate: 12881.818181818182; oracle: 12882.790246856348\n",
      "71: 99.30% 99.47% (Oracle)\n",
      "Data trace contains too extreme anomalies\n",
      "Predicted tick rate: 12942.150537634408; oracle: 12948.225177304965\n",
      "73: 98.42% 98.60% (Oracle)\n",
      "Predicted tick rate: 12863.723150357995; oracle: 12863.11660777385\n",
      "74: 98.60% 98.77% (Oracle)\n",
      "Data trace contains too extreme anomalies\n",
      "Data trace contains too extreme anomalies\n",
      "Predicted tick rate: 12916.873449131514; oracle: 12916.289682313989\n",
      "77: 99.12% 98.77% (Oracle)\n",
      "Predicted tick rate: 12868.027210884355; oracle: 12869.223919757023\n",
      "78: 98.95% 98.77% (Oracle)\n",
      "Predicted tick rate: 12798.305084745763; oracle: 12791.536412078152\n",
      "79: 98.95% 98.95% (Oracle)\n",
      "Predicted tick rate: 13093.60465116279; oracle: 13134.534513274337\n",
      "80: 98.77% 99.47% (Oracle)\n",
      "Predicted tick rate: 12902.331002331002; oracle: 12905.081560283688\n",
      "81: 98.95% 99.30% (Oracle)\n",
      "Predicted tick rate: 12864.016736401674; oracle: 12857.330960854093\n",
      "82: 99.47% 99.47% (Oracle)\n",
      "Predicted tick rate: 12933.165829145728; oracle: 12937.565062388592\n",
      "83: 96.49% 98.25% (Oracle)\n",
      "Data trace contains too extreme anomalies\n",
      "Predicted tick rate: 12863.28125; oracle: 12859.660135132368\n",
      "85: 99.12% 99.30% (Oracle)\n",
      "Predicted tick rate: 12971.559633027522; oracle: 12982.836917562725\n",
      "86: 98.77% 98.95% (Oracle)\n",
      "Predicted tick rate: 12876.626506024097; oracle: 12880.451154529308\n",
      "87: 98.07% 98.07% (Oracle)\n",
      "Predicted tick rate: 12867.080745341615; oracle: 12864.037234042553\n",
      "88: 98.42% 98.95% (Oracle)\n",
      "Predicted tick rate: 12922.245762711864; oracle: 12925.976827094473\n",
      "89: 99.12% 99.30% (Oracle)\n",
      "Predicted tick rate: 12819.181034482759; oracle: 12812.217857142858\n",
      "90: 98.60% 98.95% (Oracle)\n",
      "Predicted tick rate: 12864.70588235294; oracle: 12872.162236159136\n",
      "91: 98.95% 99.30% (Oracle)\n",
      "Predicted tick rate: 12906.904761904761; oracle: 12912.95284677328\n",
      "92: 98.07% 98.77% (Oracle)\n",
      "Predicted tick rate: 13070.570570570571; oracle: 13105.012389380532\n",
      "93: 97.02% 97.02% (Oracle)\n",
      "Predicted tick rate: 12920.098039215687; oracle: 12922.421708185053\n",
      "94: 98.60% 99.12% (Oracle)\n",
      "Predicted tick rate: 12856.485355648536; oracle: 12855.721340388007\n",
      "95: 99.12% 99.47% (Oracle)\n",
      "Predicted tick rate: 12862.932790224033; oracle: 12859.13074204947\n",
      "96: 99.65% 99.65% (Oracle)\n",
      "Predicted tick rate: 12866.28959276018; oracle: 12862.508865248226\n",
      "97: 98.42% 99.12% (Oracle)\n",
      "Predicted tick rate: 13100.0; oracle: 12794.97168141593\n",
      "Boundaries are highly unreliable. 1\n",
      "Predicted tick rate: 12826.488706365502; oracle: 12825.054167961172\n",
      "99: 98.60% 99.30% (Oracle)\n",
      "Predicted tick rate: 12958.333333333334; oracle: 12961.302654867257\n",
      "Too many iteration boundaries!\n",
      "Predicted tick rate: 12862.5; oracle: 12865.693262411347\n",
      "101: 98.42% 99.12% (Oracle)\n",
      "Predicted tick rate: 12897.254004576658; oracle: 12910.00892857143\n",
      "102: 97.54% 98.60% (Oracle)\n",
      "Predicted tick rate: 12831.853281853282; oracle: 12831.226148409894\n",
      "103: 98.77% 98.95% (Oracle)\n",
      "Predicted tick rate: 12844.920993227992; oracle: 12846.575786725774\n",
      "104: 98.60% 99.30% (Oracle)\n",
      "Predicted tick rate: 12979.342723004695; oracle: 12993.449557522124\n",
      "105: 97.89% 98.60% (Oracle)\n",
      "Predicted tick rate: 12940.238095238095; oracle: 12948.658995574566\n",
      "106: 99.30% 99.82% (Oracle)\n",
      "Predicted tick rate: 12855.298651252408; oracle: 12853.04609929078\n",
      "107: 99.12% 99.12% (Oracle)\n",
      "Predicted tick rate: 12866.101694915254; oracle: 12870.0455427583\n",
      "108: 97.89% 99.30% (Oracle)\n",
      "Predicted tick rate: 12859.381044487427; oracle: 12858.334513274336\n",
      "109: 98.60% 100.00% (Oracle)\n",
      "Predicted tick rate: 12842.685370741483; oracle: 12841.532006493086\n",
      "110: 98.95% 99.82% (Oracle)\n",
      "Predicted tick rate: 12818.565400843881; oracle: 12813.253546099291\n",
      "111: 98.77% 100.00% (Oracle)\n",
      "Predicted tick rate: 12801.162790697674; oracle: 12799.060283687943\n",
      "112: 99.12% 99.65% (Oracle)\n"
     ]
    }
   ],
   "source": [
    "# evaluate accuracy with 100 traces\n",
    "corret_anom = True\n",
    "iters = 100\n",
    "results = []\n",
    "ideal_results = []\n",
    "wrongs = []\n",
    "ideal_wrongs = []\n",
    "\n",
    "data_l, oracle_l = load_raw_data(VER, 'eval')\n",
    "b_model = load_boundary_model(VER, width, period)\n",
    "s_model = load_signal_model(VER, n_samples=n_samples, period=period)\n",
    "cnt, valid = 0, 0\n",
    "while valid < iters and cnt < min(len(data_l), len(oracle_l)):\n",
    "    data, oracle = data_l[cnt], oracle_l[cnt]\n",
    "    cnt += 1\n",
    "\n",
    "    try:\n",
    "        dt = DataTrace(data, oracle, std_filter_fact(), margin=2*width, period=period,\n",
    "                        correct_anomaly=corret_anom, anomaly_thresh=anom_thresh)\n",
    "    except ValueError as e:\n",
    "        print(e)\n",
    "        continue\n",
    "\n",
    "    b_pred = BoundaryPredictor(dt, b_model, width=width)\n",
    "    try:\n",
    "        b_pred.predict()\n",
    "    except ValueError as e:\n",
    "        print(e)\n",
    "        acc = None\n",
    "    else:\n",
    "        s_pred = SignalPredictor(dt, s_model, b_pred.boundaries, n_samples=n_samples)\n",
    "        s_pred.predict()\n",
    "        acc = s_pred.acc\n",
    "        results.append(s_pred.acc)\n",
    "        wrongs.append((s_pred.wrong_stats, s_pred.off_by_one))\n",
    "\n",
    "    if acc is not None:\n",
    "        s_pred_ideal = SignalPredictor(dt, s_model, dt.iter_boundaries, n_samples=n_samples)\n",
    "        s_pred_ideal.predict()\n",
    "        ideal_results.append(s_pred_ideal.acc)\n",
    "        ideal_wrongs.append((s_pred_ideal.wrong_stats, s_pred_ideal.off_by_one))\n",
    "        print(f'{cnt}: Acc: {acc:.2%} Oracle Acc: {s_pred_ideal.acc:.2%}')\n",
    "        valid += 1\n",
    "\n",
    "with open('results/mont_acc.pickle', 'wb') as f:\n",
    "    pickle.dump(dict(results=results, ideal_results=ideal_results,\n",
    "                     wrongs=wrongs, ideal_wrongs=ideal_wrongs), f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy: 98.49% +- 0.22% (P=0.95)\n",
      "Accuracy (Oracle): 98.98% +- 0.15% (P=0.95)\n"
     ]
    }
   ],
   "source": [
    "# print accuracy summary\n",
    "\n",
    "with open('results/mont_acc.pickle', 'rb') as f:\n",
    "    _res = pickle.load(f)\n",
    "    results = _res['results']\n",
    "    ideal_results = _res['ideal_results']\n",
    "    wrongs = _res['wrongs']\n",
    "    ideal_wrongs = _res['ideal_wrongs']\n",
    "\n",
    "f = [r for r, w in zip(results, wrongs) if not w[1]]\n",
    "se = np.std(f) / np.sqrt(len(f) - 1)\n",
    "mean = np.mean(f)\n",
    "print(f'Accuracy: {mean:.2%} +- {2 * se:.2%} (P=0.95)')\n",
    "\n",
    "se = np.std(ideal_results) / np.sqrt(len(ideal_results) - 1)\n",
    "mean = np.mean(ideal_results)\n",
    "print(f'Accuracy (Oracle): {mean:.2%} +- {2 * se:.2%} (P=0.95)')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.10 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
